
// ===============================================================================================================
// -*- C++ -*-
//
// Texture.cpp - Texture interfaces and an OpenGL texture management.
//
// Copyright (c) 2011 Guilherme R. Lampert
// guilherme.ronaldo.lampert@gmail.com
//
// This code is licenced under the MIT license.
//
// This software is provided "as is" without express or implied
// warranties. You may freely copy and compile this source into
// applications you distribute provided that the copyright text
// above is included in the resulting source code.
//
// ===============================================================================================================

#include <Texture.hpp>
#include <Common.hpp>

// =========================================================
// Texture Class Implementation
// =========================================================

Texture::Texture(void)
: ReferenceCountable(), flags(Texture::DEFAULT), standardCoordSystem(true), failBit(true)
{
}

bool Texture::Fail(void) const
{
	return (failBit);
}

bool Texture::StdCoordSystem(void) const
{
	return (standardCoordSystem);
}

const std::string & Texture::FileName(void) const
{
	return (fileName);
}

unsigned long Texture::AddRef(void) const
{
	return (++refCount);
}

unsigned long Texture::Release(void) const
{
	if (--refCount == 0)
	{
		// Time to die...
		delete this;
		return (0);
	}

	return (refCount);
}

unsigned long Texture::ReferenceCount(void) const
{
	return (refCount);
}

// =========================================================
// GL_2D_Texture Class Implementation
// =========================================================

GL_2D_Texture::GL_2D_Texture(const Image * img, TextureFlags imgFlags) : Texture(), id(0)
{
	try {
		failBit = Create(img, imgFlags);
	}
	catch (...) {
		failBit = true;
	}

	if (failBit)
	{
		LOG_ERROR("Couldn't create texture 2D from image " << img->FileName());
	}
}

GL_2D_Texture::~GL_2D_Texture(void)
{
	// Delete texture object
	if (glIsTexture(id))
	{
		glDeleteTextures(1, &id);
	}
}

void GL_2D_Texture::Bind(unsigned int texUnit) const
{
	if (!failBit)
	{
		glActiveTexture(GL_TEXTURE0 + texUnit);
		glBindTexture(GL_TEXTURE_2D, id);
	}
}

bool GL_2D_Texture::Create(const Image * img, TextureFlags imgFlags)
{
	if (!img || img->Fail())
	{
		return (true); // Return true to set the fail bit.
	}

	// Fill texture's vars:
	fileName = img->FileName();
	flags = imgFlags;
	standardCoordSystem = img->StdCoordSystem();

	// Generate a texture id:
	glGenTextures(1, &id);
	glBindTexture(GL_TEXTURE_2D, id);

	// Setup texture filters:
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

	MemoryBuffer * pixelBuf = img->Pixels();

	// Build the texture and generate mipmaps:
	if (GLEW_SGIS_generate_mipmap && img->IsPowerOfTwo())
	{
		// Hardware mipmap generation (Nice):
		glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS, GL_TRUE);
		glHint(GL_GENERATE_MIPMAP_HINT_SGIS, GL_NICEST);

		glTexImage2D(GL_TEXTURE_2D, 0, GetCompressionFormat(img->Components()),
		img->Width(), img->Height(), 0, img->Format(), GL_UNSIGNED_BYTE, pixelBuf->GetBufferPointer());
	}
	else
	{
		// No hardware mipmap generation support, fall back to the good old gluBuild2DMipmaps() function:
		gluBuild2DMipmaps(GL_TEXTURE_2D, GetCompressionFormat(img->Components()),
		img->Width(), img->Height(), img->Format(), GL_UNSIGNED_BYTE, pixelBuf->GetBufferPointer());
	}

	pixelBuf->Release();

	// Does texture creation succeeded?

	GLenum err = glGetError();

	if (GL_NO_ERROR == err)
	{
		return (false); // Return no error, failBit will be clear.
	}
	else
	{
		LOG_ERROR("OpenGL Error: " << reinterpret_cast<const char *>(gluErrorString(err)) << " in GL_2D_Texture::Create()");
		return (true);
	}
}

GLint GL_2D_Texture::GetCompressionFormat(GLint components) const
{
	if (flags & Texture::COMPRESS)
	{
		if (!GLEW_EXT_texture_compression_s3tc || !GLEW_ARB_texture_compression)
		{
			// No compression possible on this target machine
			return (components);
		}

		switch (components)
		{
		case 1:
			return (GL_COMPRESSED_LUMINANCE);

		case 2:
			return (GL_COMPRESSED_LUMINANCE_ALPHA);

		case 3:
			return (GL_COMPRESSED_RGB);

		case 4:
			return (GL_COMPRESSED_RGBA);

		default:
			// Unknown internal format!
			LOG_ERROR("GL_2D_Texture::GetCompressionFormat(): Bad internal format");
			return (components);
		}
	}
	else
	{
		return (components);
	}
}